/**************************************************************************************************
* Title: SumSimulationManager.cpp
* Author: Gael Huber
* Description: Manages primary game loop and manages subsystem start up and shutdown.
**************************************************************************************************/
#include "SumSimulationManager.h"

/**************************************************************************************************
* Initialize the singleton instance of this class to 0
**************************************************************************************************/
template <> SimulationManager* Singleton<SimulationManager>::singleton = 0;

/**************************************************************************************************
* Constructor
**************************************************************************************************/
SimulationManager::SimulationManager(void) {
}

/**************************************************************************************************
* Destructor
**************************************************************************************************/
SimulationManager::~SimulationManager(void) {
}

/**************************************************************************************************
* Start up all engine subsystems
**************************************************************************************************/
void SimulationManager::startUp(int nCmdShow) {
	// Seed random numbers
	seed();

	// Initialize the global memory manager first so necessary pre-allocations can be made
	globalMemoryManager = new GlobalMemoryManager();
	globalMemoryManager->startUp();

	// Initialize resources
	resourceManager = new ResourceManager();
	resourceManager->startUp();

	// Initialize configuration
	configurationManager = new ConfigurationManager();
	configurationManager->startUp();

	// Initialize rendering
	renderManager = new RenderManager();
	renderManager->startUp(nCmdShow);

	// Initialize input
	inputManager = new InputManager();
	inputManager->startUp();

	// TEMP CODE
	meshLoader = new MeshLoader();
	meshLoader->load("..\\Sum Engine\\Media\\Meshes\\test.3ds");

	// Simulation is ready to be run, we can now start the job system
	SetRunSimulation(true);

	// Initialize the job manager last to ensure that all jobs can be properly finished upon shut-down
	jobManager = new JobManager();
	jobManager->startUp(50);
}

/**************************************************************************************************
* Shut down all engine subsystems
**************************************************************************************************/
void SimulationManager::shutDown(void) {
	// Show the cursor
	ShowCursor(TRUE);
	
	jobManager->shutDown();
	delete jobManager;
	
	inputManager->shutDown();
	delete inputManager;

	renderManager->shutDown();
	delete renderManager;

	configurationManager->shutDown();
	delete configurationManager;

	resourceManager->shutDown();
	delete resourceManager;

	globalMemoryManager->shutDown();
	delete globalMemoryManager;
}

/**************************************************************************************************
* Run the simulation game loop
**************************************************************************************************/
void SimulationManager::run(void) {
	gameLoop();
}

/**************************************************************************************************
* Engine game loop
**************************************************************************************************/
void SimulationManager::gameLoop(void) {
	// Handles for engine components
	Job renderJob;	AssignRenderingFunction(renderJob);
	Job inputJob;	AssignInputFunction(inputJob);

	// Define a struct to hold a Windows event message
	MSG msg;

	// While the windows message queue is populated and the program has not been exited
	while(GetRunSimulation()) {
		// Check for any Windows messages
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			// Translate keystroke messages to a compatible format
			TranslateMessage(&msg);

			// Send the message to the WindowProcess function
			DispatchMessage(&msg);
		}

		// Request input from the hardware
		RequestInput(inputJob);

		// Wait for rendering to finish
		WaitForRendering(renderJob);

		// Wait for input to finish
		WaitForInput(inputJob);
		
		//---------------------------------------------
		// TEMP CODE
		
		// Change camera orientation!
		XMVECTOR v = GetView().r[3];
		if(GetKeyboardCode(DIK_LEFT)) {
			v.m128_f32[0] -= 0.01f;
		}
		if(GetKeyboardCode(DIK_RIGHT)) {
			v.m128_f32[0] += 0.01f;
		}
		if(GetKeyboardCode(DIK_UP)) {
			v.m128_f32[1] += 0.01f;
		}
		if(GetKeyboardCode(DIK_DOWN)) {
			v.m128_f32[1] -= 0.01f;
		}
		GetView() = XMMatrixMultiply(XMMatrixTranslation(v.m128_f32[0], v.m128_f32[1], v.m128_f32[2]), 
								XMMatrixRotationRollPitchYaw(GetMouseState().lY * 0.003f, GetMouseState().lX * 0.003f, 0.0f));

		// END TEMP CODE
		//---------------------------------------------

		// Render the scene
		RequestRendering(renderJob);


		/* PROPOSED GAME LOOP
		// Get input from the hardware
		RequestInput(inputJob);

		// Ensure rendering has finished before updating objects
		WaitForRendering(renderJob);

		// Wait for AI and sound
		WaitForAI(aiJob);
		WaitForAudio(audioJob);

		// Wait for input
		WaitForInput(inputJob);

		// Update game objects to reflect calculations made in previous tick. This also analyzes the game
		// context and handles input callback.
		RequestObjectUpdate(objectUpdateJob);

		// Wait for all object update tasks to finish (game objects can be batched internally) but we 
		// must still wait for all objects to be fully updated. These updates include movement logic and
		// actually following through with AI steps for entities if AI exists. It updates relevant 
		// individual objects with game logic.
		WaitForObjectUpdate(objectUpdateJob);

		// Request animation
		RequestAnimation(animationJob);

		// Request physics
		RequestPhysics(physicsJob);

		// AI request
		RequestAI(aiJob);

		// Request audio
		RequestAudio(audioJob);

		// Wait for physics and animation jobs to finish before we begin rendering
		WaitForAnimation(animationJob);
		WaitForPhysics(physicsJob);

		// Start rendering
		RequestRendering(renderJob);*/
	}

	// Clear out job queue
	JobManager::getSingletonPtr()->clearJobs();

	// Wait for any requests to finish to prevent faulty access attempts to functions
	WaitForRendering(renderJob);
	//WaitForAnimation(animationJob);
	//WaitForPhysics(physicsJob);
	//WaitForAI(aiJob);
	//WaitForAudio(audioJob);
}